#include "map.h"

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

cell** map = NULL;
int game_over = 0;

/**
 * @brief 动态分配二维地图内存
 *
 * 该函数为全局变量 map 动态分配一个大小为 rows x cols 的二维数组，
 * 每个元素类型为 Cell。使用 malloc 和 calloc 来分别为行指针和每行数据分配内存，
 * 并初始化所有 Cell 内容为 0。
 *
 * @param rows 地图的行数
 * @param cols 地图的列数
 */
void allocateMap(int rows, int cols) {
    // 为 map 分配行指针数组空间（每一行的指针）
    map = (cell**)malloc(rows * sizeof(cell*));
    if (map == NULL) {
        fprintf(stderr, "Failed to allocate memory for map rows.\n");
        exit(EXIT_FAILURE);
    }
    // 为每一行分配 cols 个 Cell 的空间，并初始化为 0
    for (int i = 0; i < rows; i++) {
        map[i] = (cell*)calloc(cols, sizeof(cell));
        if (map[i] == NULL) {
            fprintf(stderr, "Failed to allocate memory for map rows.\n");
            exit(EXIT_FAILURE);
        }
    }
}

/**
 * @brief 在地图中随机放置地雷
 *
 * 该函数在指定的地图范围内随机生成地雷位置，并确保每个位置只放置一个地雷。
 * 使用rand()函数生成随机坐标，并通过检查is_mine属性避免重复放置。
 * 放置的地雷数量由参数num_mines指定。
 *
 * @param rows 地图的行数，用于限制随机坐标的X轴范围
 * @param cols 地图的列数，用于限制随机坐标的Y轴范围
 * @param num_mines 需要放置的地雷总数
 */
void placeMines(int rows, int cols, int num_mines) {
    int placed = 0;
    while (placed < num_mines) {
        int x = rand() % rows;
        int y = rand() % cols;
        if (!map[x][y].is_mine) {
            map[x][y].is_mine = 1;
            placed++;
        }
    }
}

/**
 * @brief 计算地图中每个非地雷格子周围的地雷数量
 *
 * 该函数遍历地图中的每一个格子，对于非地雷格子，检查其周围8个相邻格子，
 * 统计其中地雷的数量，并将结果存入该格子的 adjacent_mines 字段。
 * 该函数应在放置地雷（placeMines）后调用，以确保正确计算相邻地雷数。
 *
 * @param rows 地图的行数，用于边界检查
 * @param cols 地图的列数，用于边界检查
 */
void calculateAdjacency(int rows, int cols) {
    // 遍历地图的每一个格子
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            // 如果当前格子是地雷，跳过计算（地雷格子不需要统计周围地雷数）
            if (map[i][j].is_mine) {
                continue;
            }
            int count = 0;  // 用于统计周围地雷的数量
            // 检查当前格子的周围8个相邻格子（通过dx和dy的偏移量实现）
            for (int dx = -1; dx <= 1; dx++) {
                for (int dy = -1; dy <= 1; dy++) {
                    int ni = i + dx;  // 相邻格子的行坐标
                    int nj = j + dy;  // 相邻格子的列坐标
                    // 检查相邻格子是否在地图范围内，并且是否是地雷
                    if (ni > 0 && ni < rows && nj > 0 && nj < cols &&
                        map[ni][nj].is_mine) {
                        count++;  // 如果是地雷，计数器加1
                    }
                }
            }
            // 将统计的地雷数量存入当前格子的 adjacent_mines 字段
            map[i][j].adjacent_mines = count;
        }
    }
}

void init_map(int rows, int cols, int num_mines) {
    // srand(time(NULL)); 的作用是 基于当前时间初始化随机数种子，确保 rand()
    // 在每次程序运行时生成不同的随机数序列。
    srand(time(NULL));
    allocateMap(rows, cols);
    placeMines(rows, cols, num_mines);
    calculateAdjacency(rows, cols);
}

void print_map(int rows, int cols, int reveal_all) {
    // 打印列坐标标题（0,1,2...）
    printf("   ");
    for (int j = 0; j < cols; j++) {
        printf("%2d ", j);  // 对齐输出列号
    }
    printf("\n");
    // 遍历每一行
    for (int i = 0; i < rows; i++) {
        printf("%2d ", i);  // 打印行号
        // 遍历每一列
        for (int j = 0; j < cols; j++) {
            cell c = map[i][j];
            // 根据reveal_all或格子状态决定显示内容
            if (reveal_all || c.is_revealed) {
                if (c.is_mine) {
                    printf(" * ");  // 地雷
                } else if (c.adjacent_mines > 0) {
                    printf(" %d ", c.adjacent_mines);  // 周围地雷数
                } else {
                    printf("   ");  // 空白格子
                }
            } else if (c.is_flagged) {
                printf(" F ");  // 标记为地雷
            } else {
                printf(" . ");  // 未揭开状态
            }
        }
        printf("\n");
    }
}

/**
 * @brief 递归揭开空白格子及其相邻空白区域
 *
 * 该函数用于当玩家点击一个空白格子（周围无地雷）时，自动揭开该格子及其周围所有相邻的空白格子，
 * 直到遇到周围有地雷的格子为止。这是扫雷游戏中的标准行为，可帮助玩家快速展开大片安全区域。
 *
 * 函数执行流程：
 * 1. 首先检查坐标是否越界
 * 2. 检查格子是否已被标记或已揭开
 * 3. 揭开当前格子
 * 4. 如果当前格子周围无地雷，则递归检查周围8个相邻格子
 *
 * @param x 要揭开的格子的行坐标（0-based）
 * @param y 要揭开的格子的列坐标（0-based）
 * @param rows 地图总行数（用于边界检查）
 * @param cols 地图总列数（用于边界检查）
 */
void reveal_empty(int x, int y, int rows, int cols) {
    // 边界检查：确保坐标在地图范围内
    if (x < 0 || x >= rows || y < 0 || y >= cols) {
        return;
    }
    // 检查格子是否已被标记或已揭开，如果是则不再处理
    if (map[x][y].is_flagged || map[x][y].is_revealed) {
        return;
    }
    // 揭开当前格子
    map[x][y].is_revealed = 1;
    // 如果当前格子周围没有地雷，则递归检查周围8个相邻格子
    if (map[x][y].adjacent_mines == 0) {
        // 遍历周围8个方向（dx和dy从-1到1）
        for (int dx = -1; dx <= 1; dx++) {
            for (int dy = -1; dy <= 1; dy++) {
                // 排除当前格子本身（dx=0且dy=0的情况）
                if (dx != 0 || dy != 0) {
                    // 递归揭开相邻格子
                    reveal_empty(x + dx, y + dy, rows, cols);
                }
            }
        }
    }
}

void reveal_cell(int x, int y, int rows, int cols) {
    // 边界检查：确保坐标在地图范围内
    if (x < 0 || x >= rows || y < 0 || y >= cols) {
        return;  // 无效坐标直接返回
    }
    // 检查格子是否已被标记或已揭开
    if (map[x][y].is_flagged || map[x][y].is_revealed) {
        return;  // 已处理过的格子不再操作
    }
    // 地雷检查
    if (map[x][y].is_flagged) {
        game_over = 1;  // 触发游戏结束状态
    } else {
        reveal_empty(x, y, rows, cols);
    }
}

void toggle_flag(int x, int y) {
    // 仅当格子未被揭开时，才允许切换标记状态
    if (!map[x][y].is_revealed) {
        // 反转标记状态：1变0，0变1
        map[x][y].is_flagged = !map[x][y].is_flagged;
    }
}